package com.wgo.precise.server.wiring;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;

import remato.domain.common.RequirementSystem;

import com.wgo.bpot.common.transport.util.Assert;
import com.wgo.bpot.common.transport.util.MethodNamingConvention;
import com.wgo.bpot.common.transport.util.ReflectionHelper;
import com.wgo.bpot.domain.common.User;
import com.wgo.bpot.domain.server.ejb.UserEjb;
import com.wgo.bpot.server.bpot_users.UserType;
import com.wgo.bpot.server.configuration.exception.InvalidUserRoleException;
import com.wgo.bpot.server.configuration.impl.UsersFileServicesImpl;
import com.wgo.bpot.server.persist.Persistent;
import com.wgo.bpot.server.persist.hibernate.HibernatePersistService;
import com.wgo.precise.privilege.PreciseUserRole;


/**
 * Persistence is handled outside this class, as {@link RequirementSystem} is 
 * present.
 * 
 * @author Petter L. H. Eide
 * @version $Id: PreciseUsersFileServices.java 540 2008-01-09 19:42:31Z petter.eide $
 */
public class PreciseUsersFileServices extends UsersFileServicesImpl {

	private static final transient Logger log = Logger.getLogger(PreciseUsersFileServices.class);

	private final HibernatePersistService<Persistent> hibernatePersistService;
	
	public PreciseUsersFileServices(HibernatePersistService<Persistent> hibernatePersistService) {
		super();
		this.hibernatePersistService = hibernatePersistService;
	}

	/*
	 * (non-Javadoc)
	 * @see com.wgo.bpot.server.configuration.impl.UsersFileServicesImpl#setUserRole(com.wgo.bpot.domain.common.User, int)
	 */
	@Override
	protected void setUserRole(User user, int userRole) {
		try {
			user.setUserRole(PreciseUserRole.values()[userRole]);
		} catch (IndexOutOfBoundsException e ){
			throw new InvalidUserRoleException(2);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see com.wgo.bpot.server.configuration.impl.UsersFileServicesImpl#createUserFromUserType(com.wgo.bpot.server.bpotUsers.UserType)
	 */
	@Override
	protected User createUserFromUserType(UserType userType) {
		User user = super.createUserFromUserType(userType);
		hibernatePersistService.findDbRootSystem(RequirementSystem.class).addUser(user); // TODO: could be cached for speed optimization
		log.info("Read user " + user.getUserName() + " from file.");
		return user;
	}

	/* (non-Javadoc)
	 * @see com.wgo.bpot.server.configuration.impl.UsersFileServicesImpl#createNewUser()
	 */
	@Override
	protected User createNewUser() {
		return new UserEjb();
	}

	public boolean updatePersistedUsersFromFile(){
		return updatePersistedUsersFromFile(PreciseInitializationServiceImpl.PRECISE_USER_FILE);
	}
	
	/**
	 * @see PreciseUsersFileServices#createUserFromUserType(UserType) 
	 * 		- new users is already added to {@link RequirementSystem}
	 */
	public boolean updatePersistedUsersFromFile(String filename){
		Map<String, User> usernameToUser = new HashMap<String, User>();
		RequirementSystem requirementSystem = hibernatePersistService.findDbRootSystem(RequirementSystem.class);
		for (User user : requirementSystem.getUsers()){
			usernameToUser.put(user.getUserName(), user);
		}
		boolean result = false;
		int numUpdatedUsers = 0;
		int numNewUsers = 0;
		for (User user : getUsersFromFileQuiet(filename)){
			User oldUser = usernameToUser.remove(user.getUserName());
			if (null != oldUser) {
				updateUser(oldUser,user);
				requirementSystem.removeUser(user);
				numUpdatedUsers++;
			} else {
				numNewUsers++;
			}
			result = true;
		}
		int numRemovedUsers = 0;
		for (User user : usernameToUser.values()){
			requirementSystem.removeUser(user);
			numRemovedUsers++;
		}
		log.info("Persisted users up to date with '" + filename + "'." 
				+ " Updated users: " + numUpdatedUsers 
				+ " Removed users: " + numRemovedUsers 
				+ " New Users: " + numNewUsers);
		return result;
	}

	private User updateUser(User targetUser, User sourceUser) {
		for (Method method : User.class.getDeclaredMethods()){
			// this state should never occur could remove to improve performance:
			Assert.assertTrue(0 == targetUser.getUserName().compareToIgnoreCase(sourceUser.getUserName()), new IllegalStateException("Trying to change username not equal when updating from XML file.("+targetUser.getUserName() + "!=" + sourceUser.getUserName()+")"));
			MethodNamingConvention methodType = MethodNamingConvention.getMethodType(method);
			if (MethodNamingConvention.GET_METHOD == methodType ) {
				Method setterMethod = methodType.retriveSetterMethod(User.class, method);
				if (null != setterMethod) {
					Object result = ReflectionHelper.invokeMethod(sourceUser, method);
					ReflectionHelper.invokeMethod(targetUser, setterMethod, result);
				}
			}
		}
		return targetUser;
	}
	
}
